package org.daochong.lang.database;

import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.daochong.lang.JdbcTemplate;
import org.daochong.lang.Page;
import org.daochong.lang.PageResult;

public class BaseDatabase implements Database {

	protected String varchar = "VARCHAR";
	protected String text = "TEXT";

	public String getName() {
		return this.getClass().getSimpleName().toLowerCase();
	}

	public String getColumnType(Object val) {
		return getColumnType(val, 0, 0);
	}

	public String getColumnType(Object val, int length) {
		return getColumnType(val, length, 0);
	}

	public String getColumnType(Object val, int length, int precision) {

		if (val instanceof Boolean || val.getClass().equals(boolean.class)) {
			return "TINYINT(1)";
		}

		if (val instanceof Integer || val.getClass().equals(int.class)) {
			if (length == 0)
				return "INT";
			else
				return "INT(" + length + ")";
		}

		if (val instanceof Long || val.getClass().equals(long.class)) {
			if (length == 0)
				return "BIGINT";
			else
				return "BIGINT(" + length + ")";
		}

		if (val instanceof Short || val.getClass().equals(short.class)) {
			if (length == 0)
				return "SMALLINT";
			else
				return "SMALLINT(" + length + ")";
		}

		if (val instanceof Float || val.getClass().equals(float.class)) {
			if (length > 0 && precision > 0) {
				return "FLOAT(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "FLOAT(" + length + ",6)";
			} else if (length == 0 && precision > 0) {
				return "FLOAT(32," + precision + ")";
			} else {
				return "FLOAT";
			}
		}

		if (val instanceof Double || val.getClass().equals(double.class)) {
			if (length > 0 && precision > 0) {
				return "DOUBLE(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "DOUBLE(" + length + ",6)";
			} else if (length == 0 && precision > 0) {
				return "DOUBLE(32," + precision + ")";
			} else {
				return "DOUBLE";
			}
		}

		if (val instanceof Byte || val.getClass().equals(byte.class)) {
			return "TINYINT";
		}

		if (val instanceof BigInteger) {
			return "DECIMAL(32,0)";
		}

		if (val instanceof BigDecimal) {
			if (length > 0 && precision > 0) {
				return "DECIMAL(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "DECIMAL(" + length + ",0)";
			} else if (length == 0 && precision > 0) {
				return "DECIMAL(32," + precision + ")";
			} else {
				return "DECIMAL";
			}
		}

		if (val instanceof String) {
			if (length == 0) {
				length = ((String) val).length();
				if (length % 10 > 0) {
					length = (int) Math.pow(10, String.valueOf(length).length());
				}
				length = Math.max(length, 50);
			}
			if (length < 4000) {
				return this.varchar + "(" + length + ")";
			} else {
				return this.text;
			}
		}

		if (val instanceof Timestamp) {
			return "TIMESTAMP";
		}

		if (val instanceof Date) {
			return "DATETIME";
		}

		if (val instanceof byte[] || val instanceof char[] || val instanceof InputStream) {
			return "BLOB";
		}

		return "BLOB";
	}

	public String getColumnType(Class<?> clazz) {
		return getColumnType(clazz, 0, 0);
	}

	public String getColumnType(Class<?> clazz, int length) {
		return getColumnType(clazz, length, 0);
	}

	public String getColumnType(Class<?> clazz, int length, int precision) {
		if (clazz.equals(Boolean.class) || clazz.equals(boolean.class)) {
			return "TINYINT(1)";
		}

		if (clazz.equals(Integer.class) || clazz.equals(int.class)) {
			if (length == 0)
				return "INT";
			else
				return "INT(" + length + ")";
		}

		if (clazz.equals(Long.class) || clazz.equals(long.class)) {
			if (length == 0)
				return "BIGINT";
			else
				return "BIGINT(" + length + ")";
		}

		if (clazz.equals(Short.class) || clazz.equals(short.class)) {
			if (length == 0)
				return "SMALLINT";
			else
				return "SMALLINT(" + length + ")";
		}

		if (clazz.equals(Float.class) || clazz.equals(float.class)) {
			if (length > 0 && precision > 0) {
				return "FLOAT(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "FLOAT(" + length + ",6)";
			} else if (length == 0 && precision > 0) {
				return "FLOAT(32," + precision + ")";
			} else {
				return "FLOAT";
			}
		}

		if (clazz.equals(Double.class) || clazz.equals(double.class)) {
			if (length > 0 && precision > 0) {
				return "DOUBLE(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "DOUBLE(" + length + ",6)";
			} else if (length == 0 && precision > 0) {
				return "DOUBLE(32," + precision + ")";
			} else {
				return "DOUBLE";
			}
		}

		if (clazz.equals(Byte.class) || clazz.equals(byte.class)) {
			return "TINYINT";
		}

		if (clazz.equals(BigInteger.class)) {
			return "DECIMAL(32,0)";
		}

		if (clazz.equals(BigDecimal.class)) {
			if (length > 0 && precision > 0) {
				return "DECIMAL(" + length + "," + precision + ")";
			} else if (length > 0 && precision == 0) {
				return "DECIMAL(" + length + ",0)";
			} else if (length == 0 && precision > 0) {
				return "DECIMAL(32," + precision + ")";
			} else {
				return "DECIMAL";
			}
		}

		if (clazz.equals(String.class)) {
			if (length == 0) {
				return this.varchar + "(50)";
			} else if (length < 4000) {
				return this.varchar + "(" + length + ")";
			} else {
				return this.text;
			}
		}

		if (clazz.equals(Timestamp.class)) {
			return "TIMESTAMP";
		}

		if (clazz.equals(Date.class) || Date.class.equals(clazz.getSuperclass())) {
			return "DATETIME";
		}

		if (clazz.equals(byte[].class) || clazz.equals(char[].class) || clazz.equals(InputStream.class)) {
			return "BLOB";
		}

		return "BLOB";
	}

	public Map<String, Column> getColumns(String tableName, JdbcTemplate jdbc) throws SQLException {
		Map<String, Column> re = new LinkedHashMap<String, Column>();
		Connection cn = null;
		Statement st = null;
		ResultSet rs = null;
		try {
			if (jdbc.getDataSource() == null)
				throw new RuntimeException("datasource is null");
			cn = jdbc.getDataSource().getConnection();
			st = cn.createStatement();
			rs = st.executeQuery("select * from " + tableName + " where 1=0");
			ResultSetMetaData rmd = rs.getMetaData();
			for (int i = 1; i <= rmd.getColumnCount(); i++) {
				Column column = new Column();
				column.setName(rmd.getColumnName(i).toUpperCase());
				column.setType(rmd.getColumnTypeName(i));
				column.setLength(rmd.getColumnDisplaySize(i));
				re.put(column.getName(), column);
			}
			rs.close();
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			if (st != null) {
				try {
					st.close();
				} catch (Throwable e) {

				}
			}
			if (cn != null) {
				try {
					cn.close();
				} catch (Throwable e) {

				}
			}
		}
		return re;
	}

	public PageResult<Map<String, Object>> listObjects(JdbcTemplate jdbc, Page page, String sql, Object... params)
			throws SQLException {
		PageResult<Map<String, Object>> result = new PageResult<Map<String, Object>>();
		result.setPage(page.getPage());
		result.setPageSize(page.getPageSize());
		if (result.getPage() <= 0) {
			result.setPage(1);
		}
		if (result.getPageSize() <= 0) {
			result.setPageSize(20);
		}
		List<Map<String, Object>> list = jdbc.listObjects(sql, params);
		if (list != null && list.size() > 0) {
			result.setCount(list.size());
			list = list.subList((result.getPage() - 1) * result.getPageSize(), result.getPage() * result.getPageSize());
			result.setData(list);
		}
		return result;
	}

	public List<String> getTableList(JdbcTemplate jdbc) throws SQLException {
		throw new RuntimeException("Database " + getName() + " not support load tables");
	}

}
